package com.fast.cron.db;

import org.apache.commons.dbutils.BasicRowProcessor;
import org.apache.commons.dbutils.BeanProcessor;
import org.apache.commons.dbutils.DbUtils;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.apache.commons.dbutils.handlers.MapListHandler;
import org.apache.commons.dbutils.handlers.ScalarHandler;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;

/**
 * 数据库操作帮助类
 *
 * @author :leo.jie
 * @version :v1.0
 * @date :2018/9/29 13:30
 */
@SuppressWarnings({"rawtypes", "unchecked"})
public class DbHelperFactoryImpl implements DbHelperFactory {
    private final ThreadLocal<Connection> THREAD_LOCAL = new ThreadLocal<>();
    private final QueryRunner RUNNER = new QueryRunner();


    private Connection getConnection() {
        Connection connection = THREAD_LOCAL.get();
        if (null == connection) {
            try {
                connection = HikariDataSourceHelper.getConnection();
                THREAD_LOCAL.set(connection);
            } catch (SQLException e) {
                throw new RuntimeException("failed to get connection of mysql ... ");
            }
        }
        return connection;
    }

    private void freeConnection() {
        Connection connection = THREAD_LOCAL.get();
        if (null != connection) {
            DbUtils.closeQuietly(connection);
            THREAD_LOCAL.remove();
        }
    }

    @Override
    public List<Map<String, Object>> query(String sql, Object... para) throws SQLException {
        Connection conn = getConnection();
        List<Map<String, Object>> result = RUNNER.query(conn, sql, new MapListHandler(), para);
        if (conn.getAutoCommit()) {
            freeConnection();
        }
        return result;
    }

    @Override
    public <T> T queryOne(Class<T> cls, String sql, Map rowMap, Object... para) throws SQLException {
        Connection conn = getConnection();
        final String limitStr = " limit ";
        if (!sql.toLowerCase().contains(limitStr)) {
            sql += " limit 1";
        }
        T result = null;
        if (!rowMap.isEmpty()) {
            result = RUNNER.query(conn, sql, new BeanHandler<>(cls,
                    new BasicRowProcessor(new BeanProcessor(rowMap))), para);
        }
        if (conn.getAutoCommit()) {
            freeConnection();
        }
        return result;
    }

    @Override
    public <T> List<T> query(Class<T> cls, String sql, Map rowMap, Object... para) throws SQLException {
        Connection conn = getConnection();
        List<T> result = null;
        if (!rowMap.isEmpty()) {
            result = RUNNER.query(conn, sql, new BeanListHandler<>(cls,
                    new BasicRowProcessor(new BeanProcessor(rowMap))), para);
        }
        if (conn.getAutoCommit()) {
            freeConnection();
        }
        return result;
    }

    @Override
    public <T> List<T> query(Class<T> cls, String sql, Object... para) throws SQLException {
        Connection conn = getConnection();
        List<T> result = RUNNER.query(conn, sql, new BeanListHandler<>(cls), para);
        if (conn.getAutoCommit()) {
            freeConnection();
        }
        return result;
    }

    @Override
    public <T> T queryOne(Class<T> cls, String sql, Object... para) throws SQLException {
        Connection conn = getConnection();
        final String limitStr = " limit ";
        if (!sql.toLowerCase().contains(limitStr)) {
            sql += " limit 1";
        }
        T result = RUNNER.query(conn, sql, new BeanHandler<>(cls), para);

        if (conn.getAutoCommit()) {
            freeConnection();
        }
        return result;
    }

    @Override
    public Long queryCount(String sql, Object... para) throws SQLException {
        Connection conn = getConnection();
        Long number = RUNNER.query(conn, sql, new ScalarHandler<>(), para);
        if (conn.getAutoCommit()) {
            freeConnection();
        }
        return number;
    }

    @Override
    public int update(String sql, Object... para) throws SQLException {
        Connection conn = getConnection();
        int count = RUNNER.update(conn, sql, para);
        if (conn.getAutoCommit()) {
            freeConnection();
        }
        return count;
    }

    @Override
    public Long insert(String sql, Object... para) throws SQLException {
        Connection conn = getConnection();
        Long id = RUNNER.insert(conn, sql, new ScalarHandler<>(), para);
        if (conn.getAutoCommit()) {
            freeConnection();
        }
        return id;
    }

    @Override
    public void executeBach(String sql, Object[][] params) throws SQLException {
        Connection conn = getConnection();
        RUNNER.batch(conn, sql, params);
        if (conn.getAutoCommit()) {
            freeConnection();
        }
    }

    @Override
    public void startTransaction() throws SQLException {
        Connection conn = THREAD_LOCAL.get();
        if (null == conn) {
            conn = getConnection();
            THREAD_LOCAL.set(conn);
        }
        conn.setAutoCommit(false);
    }

    @Override
    public void commit() throws SQLException {
        Connection conn = THREAD_LOCAL.get();
        if (null != conn) {
            conn.commit();
        }
    }

    @Override
    public void rollback() throws SQLException {
        Connection conn = THREAD_LOCAL.get();
        if (null != conn) {
            conn.rollback();
        }
    }


}
